import 'dart:convert';
import 'dart:io';

import 'package:efood_multivendor/controller/splash_controller.dart';
import 'package:efood_multivendor/data/model/response/address_model.dart';
import 'package:efood_multivendor/data/model/response/error_response.dart';
import 'package:efood_multivendor/util/app_constants.dart';
import 'package:flutter/foundation.dart' as Foundation;
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as Http;
import 'package:http_parser/http_parser.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path/path.dart';
import 'package:platform_device_id/platform_device_id.dart';
import 'package:shared_preferences/shared_preferences.dart';

class ApiClient extends GetxService {
  final String appBaseUrl;
  final SharedPreferences sharedPreferences;
  static final String noInternetMessage = 'connection_failed'.tr;
  final int timeoutInSeconds = 30;

  String? token;
  Map<String, String> _mainHeaders = {
    'X-Location-Long': "",
    'X-Location-Lat': ""
  };

  ApiClient({required this.appBaseUrl, required this.sharedPreferences}) {
    token = sharedPreferences.getString(AppConstants.TOKEN);
    // print(token);
    AddressModel? _addressModel;
    try {
      _addressModel = AddressModel.fromJson(jsonDecode(
          sharedPreferences.getString(AppConstants.USER_ADDRESS) ?? "{}"));
    } catch (e) {}
    updateHeader(
        token ?? '',
        _addressModel == null ? null : _addressModel.zoneId.toString(),
        sharedPreferences.getString(AppConstants.LANGUAGE_CODE),
        sharedPreferences.getDouble(AppConstants.LOCATION_KEY_LAT),
        sharedPreferences.getDouble(AppConstants.LOCATION_KEY_LNG),
        Platform.localeName);
  }

  void updateHeader(
    String? token,
    String? zoneID,
    String? languageCode,
    double? locLat,
    double? locLong,
    String? systemLanguageCode,
  ) async {
    String deviceId = 'unknown';

    try {
      deviceId = (await PlatformDeviceId.getDeviceId)!;
    } on PlatformException {
      deviceId = 'unknown';
    }

    _mainHeaders = {
      'Content-Type': 'application/json; charset=UTF-8',
      'Authorization': 'Bearer $token',
      'X-Location-Long': locLong != null ? locLong.toString() : "",
      'X-Location-Lat': locLat != null ? locLat.toString() : "",
      'device-id': deviceId,
      'Accept-Language': Platform.localeName,
      AppConstants.ZONE_ID: zoneID ?? '',
      AppConstants.LOCALIZATION_KEY:
          languageCode ?? AppConstants.languages[0].languageCode!,
      'version': Get.find<SplashController>().currentVersion ?? '',
      'platform': '${Platform.isAndroid ? 'android' : 'ios'}',
    };
  }

  Map<String, String> _getHeader() {
    return {
      'platform': '${Platform.isAndroid ? 'android' : 'ios'}',
      if (token?.isNotEmpty == true) 'Authorization': 'Bearer $token',
    };
  }

  Future<Response> getData(String uri,
      {Map<String, dynamic>? query, Map<String, String>? headers}) async {
    try {
      Http.Response _response = await Http.get(
        Uri.parse(appBaseUrl + uri),
        headers: (headers?..addAll(_getHeader())) ?? _mainHeaders,
      ).timeout(Duration(seconds: timeoutInSeconds));
      Response response = handleResponse(_response);
      print('\x1B[33m[GET]\x1B[0m $appBaseUrl$uri');
      print('\x1B[33m[GET]\x1B[0m ${response.statusText}');
      print('\x1B[33m[GET]\x1B[0m ${response.statusCode}');
      print('\x1B[33m[GET]\x1B[0m ${response.body.toString()}');
      return response;
    } catch (e) {
      return Response(statusCode: 1, statusText: noInternetMessage);
    }
  }

  Future<Response> postData(String uri, dynamic body,
      {Map<String, String>? headers}) async {
    try {
      Http.Response _response = await Http.post(
        Uri.parse(appBaseUrl + uri),
        body: jsonEncode(body),
        headers: (headers?..addAll(_getHeader())) ?? _mainHeaders,
      ).timeout(Duration(seconds: timeoutInSeconds));
      Response response = handleResponse(_response);
      print('\x1B[33m[POST]\x1B[0m $uri');
      print('\x1B[33m[POST]\x1B[0m ${body.toString()}');
      print('\x1B[33m[POST]\x1B[0m ${response.statusText}');
      print('\x1B[33m[POST]\x1B[0m ${response.statusCode}');
      print('\x1B[33m[POST body]\x1B[0m ${response.body}');

      return response;
    } catch (e) {
      return Response(statusCode: 1, statusText: noInternetMessage);
    }
  }

  Future<Response> postMultipartData(
      String uri, Map<String, String> body, List<MultipartBody> multipartBody,
      {Map<String, String>? headers}) async {
    try {
      Http.MultipartRequest _request =
          Http.MultipartRequest('POST', Uri.parse(appBaseUrl + uri));
      _request.headers.addAll((headers?..addAll(_getHeader())) ?? _mainHeaders);
      for (MultipartBody multipart in multipartBody) {
        if (multipart.file.path.isNotEmpty) {
          if (Foundation.kIsWeb) {
            Uint8List _list = await multipart.file.readAsBytes();
            Http.MultipartFile _part = Http.MultipartFile(
              multipart.key,
              multipart.file.readAsBytes().asStream(),
              _list.length,
              filename: basename(multipart.file.path),
              contentType: MediaType('image', 'jpg'),
            );
            _request.files.add(_part);
          } else {
            File _file = File(multipart.file.path);
            _request.files.add(Http.MultipartFile(
              multipart.key,
              _file.readAsBytes().asStream(),
              _file.lengthSync(),
              filename: _file.path.split('/').last,
            ));
          }
        }
      }
      _request.fields.addAll(body);
      Http.Response _response =
          await Http.Response.fromStream(await _request.send());
      Response response = handleResponse(_response);
      return response;
    } catch (e) {
      return Response(statusCode: 1, statusText: noInternetMessage);
    }
  }

  Future<Response> putData(String uri, dynamic body,
      {Map<String, String>? headers}) async {
    try {
      Http.Response _response = await Http.put(
        Uri.parse(appBaseUrl + uri),
        body: jsonEncode(body),
        headers: (headers?..addAll(_getHeader())) ?? _mainHeaders,
      ).timeout(Duration(seconds: timeoutInSeconds));
      Response response = handleResponse(_response);
      // print('\x1B[33m[PUT]\x1B[0m $uri');
      return response;
    } catch (e) {
      return Response(statusCode: 1, statusText: noInternetMessage);
    }
  }

  Future<Response> deleteData(String uri,
      {Map<String, String>? headers}) async {
    try {
      Http.Response _response = await Http.delete(
        Uri.parse(appBaseUrl + uri),
        headers: (headers?..addAll(_getHeader())) ?? _mainHeaders,
      ).timeout(Duration(seconds: timeoutInSeconds));
      Response response = handleResponse(_response);
      //  print('\x1B[33m[DELETE]\x1B[0m $uri');
      return response;
    } catch (e) {
      return Response(statusCode: 1, statusText: noInternetMessage);
    }
  }

  Response handleResponse(Http.Response response) {
    dynamic _body;
    try {
      _body = jsonDecode(response.body);
    } catch (e) {}
    Response _response = Response(
      body: _body != null ? _body : response.body,
      bodyString: response.body.toString(),
      headers: response.headers,
      statusCode: response.statusCode,
      statusText: response.reasonPhrase,
    );
    if (_response.statusCode != 200 &&
        _response.body != null &&
        _response.body is! String) {
      if (_response.body.toString().startsWith('{errors: [{code:')) {
        ErrorResponse _errorResponse = ErrorResponse.fromJson(_response.body);
        _response = Response(
            statusCode: _response.statusCode,
            body: _response.body,
            statusText: _errorResponse.errors?[0].message);
      } else if (_response.body.toString().startsWith('{message')) {
        _response = Response(
            statusCode: _response.statusCode,
            body: _response.body,
            statusText: _response.body['message']);
      }
    } else if (_response.statusCode != 200 && _response.body == null) {
      _response = Response(statusCode: 0, statusText: noInternetMessage);
    }
    return _response;
  }

  Future<Response?> postPayment(String uri, dynamic body,
      {Map<String, String>? headers}) async {
    try {
      Http.Response _response = await Http.post(
        Uri.parse(appBaseUrl + uri),
        body: jsonEncode(body),
        headers: (headers?..addAll(_getHeader())) ?? _mainHeaders,
      ).timeout(Duration(seconds: timeoutInSeconds));
      Response response = handleResponse(_response);
      print('\x1B[33m[PAYMENT]\x1B[0m $uri');
      print('\x1B[33m[PAYMENT]\x1B[0m ${body.toString()}');
      print('\x1B[33m[PAYMENT]\x1B[0m ${response.statusText}');
      print('\x1B[33m[PAYMENT]\x1B[0m ${response.statusCode}');
      print('\x1B[33m[PAYMENT]\x1B[0m ${response.body}');
      // print('\x1B[33m[POST-PAYMENT]\x1B[0m $uri');
      return response;
    } catch (e) {
      if (e.toString().toLowerCase() != "too many requests") {
        return Response(statusCode: 1, statusText: noInternetMessage);
      }
      return null;
    }
  }
}

class MultipartBody {
  String key;
  XFile file;

  MultipartBody(this.key, this.file);
}
